﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading;

namespace Routines
{
    public static class Routines
    {
        /// <summary>
        /// Method to read data series from file
        /// </summary>
        /// <param name="fileName">File name with input data</param>
        /// <param name="length">Length of output data table</param>
        /// <param name="exclude">Nuber lines to exclude</param>
        /// <returns>Table of double values</returns>
        public static double[] GetSeries(string fileName, out long length, long exclude, long column, string separator)
        {
            // Output table length
            length = 0;

            // variable to store numbers string 
            string sNumber = string.Empty;

            // Line number
            int lineNo = 1;

            // List of double input values
            List<double> list = new List<double>();

            using (StreamReader reader = File.OpenText(fileName))
            {

                while (!reader.EndOfStream)
                {
                    // exclude this line?
                    if (lineNo <= exclude)
                    {
                        // read single number as string
                        sNumber = reader.ReadLine();


                        lineNo++;

                        if (string.IsNullOrEmpty(sNumber))
                        {
                            break;
                        }
                    }
                    else
                    {
                        break;
                    }
                }

                while (!reader.EndOfStream)
                {
                    // read single number as string
                    sNumber = reader.ReadLine();

                    char cSeparator;

                    switch (separator.ToLower())
                    {
                        case "tab":
                            {
                                cSeparator = '\t';
                                break;
                            }
                        case "comma":
                            {
                                cSeparator = ',';
                                break;
                            }
                        case "semicolon":
                            {
                                cSeparator = ';';
                                break;
                            }
                        default:
                            {
                                throw new Exception("Separator " + separator + " not allowed");
                            }
                    }
                    string[] aNumbers = sNumber.Split(new char[] { cSeparator });

                    if (aNumbers.Length < column)
                    {
                        throw new Exception("Column number " + column.ToString() + " not found");
                    }

                    sNumber = aNumbers[column - 1];

                    lineNo++;

                    double number;

                    if (double.TryParse(sNumber, out number))
                    {
                        list.Add(number);
                    }
                    else
                    {
                        throw new Exception(string.Format("GetSeries: {0} is not double number. Line number: {1}", sNumber, lineNo));
                    }
                }
            }

            length = list.Count;

            return list.ToArray();
        }

        /// <summary>
        /// Get multi time series
        /// </summary>
        /// <param name="fileName">Data file name</param>
        /// <param name="length">Length of data to read</param>
        /// <param name="exclude">How mwny lines to be exluded</param>
        /// <param name="column">Column numbers table</param>
        /// <param name="separator">Data values separator</param>
        /// <returns></returns>
        public static double[][]GetMultiSeries(string fileName, out long length, long exclude, int[] column, string separator)
        {
            // Output table length
            length = 0;

            // variable to store numbers string 
            string sNumber = string.Empty;

            // Line number
            int lineNo = 1;

            // List of double input values
            List<List<double>> list = new List<List<double>>();

            using (StreamReader reader = File.OpenText(fileName))
            {

                while (!reader.EndOfStream)
                {
                    // exclude this line?
                    if (lineNo <= exclude)
                    {
                        // read single number as string
                        sNumber = reader.ReadLine();


                        lineNo++;

                        if (string.IsNullOrEmpty(sNumber))
                        {
                            break;
                        }
                    }
                    else
                    {
                        break;
                    }
                }

                while (!reader.EndOfStream)
                {
                    // read single number as string
                    sNumber = reader.ReadLine();

                    char cSeparator;

                    switch (separator.ToLower())
                    {
                        case "tab":
                            {
                                cSeparator = '\t';
                                break;
                            }
                        case "comma":
                            {
                                cSeparator = ',';
                                break;
                            }
                        case "semicolon":
                            {
                                cSeparator = ';';
                                break;
                            }
                        default:
                            {
                                throw new Exception("Separator " + separator + " not allowed");
                            }
                    }
                    string[] aNumbers = sNumber.Split(new char[] { cSeparator });

                    List<double> row = new List<double>();

                    for (int i = 0; i < column.Length; i++)
                    {
                        double number;

                        if (i < aNumbers.Length)
                        {
                            if (double.TryParse(aNumbers[i], out number))
                            {
                                row.Add(double.Parse(aNumbers[i]));
                            }
                            else
                            {
                                throw new Exception("Expected double number");
                            }
                        }
                        else
                        {
                            throw new Exception("Column out of boundary");
                        }
                    }

                    list.Add(row);

                    lineNo++;
                }
            }

            length = list.Count;

            double[][] a = new double[column.Length][];

            long x = 0;

            for (int i = 0; i < column.Length; i++)
            {
                a[i] = new double[length];
            }

            foreach (List<double> rowItem in list)
            {
                long y = 0;

                for (int i = 0; i < column.Length; i++)
                {
                    double item = rowItem[column[i]-1];

                    a[y][x] = item;
                }

                x++;
            }

            return a;
        }

        /// <summary>
        /// Method to rescale input data. Normalize to [0,1] values
        /// </summary>
        /// <param name="input">Input data table</param>
        /// <param name="length">length of input data to normalize</param>
        /// <param name="min">nim value of input data</param>
        /// <param name="interval">interval of invput data values</param>
        public static void RescaleData(double[] input, long length, out double min, out double interval)
        {
            int i;

            min = interval = input[0];

            for (i = 1; i < length; i++)
            {
                // Min value
                if (input[i] < min) min = input[i];
                // Max value
                if (input[i] > interval) interval = input[i];
            }
            // Interval = max - min
            interval -= min;

            if (interval != 0.0)
            {
                // Rescale input data
                for (i = 0; i < length; i++)
                    input[i] = (input[i] - min) / interval;
            }
            else
            {
                throw new Exception(string.Format("RescaleData: data ranges from {0} to {1}. It makes\n\t\tno sense to continue. Exiting!\n\n", min, min + (interval)));
            }
        }

        /// <summary>
        /// Calculate average and variance for the time serie
        /// </summary>
        /// <param name="serie">Time serie</param>
        /// <param name="length">Lenght of the time serie</param>
        /// <param name="average">Average</param>
        /// <param name="variance">Variance</param>
        public static void Variance(double[] serie, long length, out double average, out double variance)
        {
            long i;
            double h;

            average = 0.00;
            variance = 0.0;

            for (i = 0; i < length; i++)
            {
                h = serie[i];
                average += h;
                variance += h * h;
            }
            average /= (double)length;

            variance = Math.Sqrt(Math.Abs((variance) / (double)length - (average) * (average)));

            if (variance == 0.0)
            {
                throw new Exception("Variance of the data is zero. Exiting!\n\n");
            }
        }
    }
}
